package rnet

import (
	"context"
	"errors"
	"fmt"

	"net"
	"net/http"
	"sync"

	"gitee.com/shanyou/sshplus/rlog"
	"github.com/sirupsen/logrus"
	"golang.org/x/sync/errgroup"
)

type HTTPServer struct {
	*http.Server
	waiter    *errgroup.Group
	waiterMux sync.Mutex
	Logger    logrus.FieldLogger
}

// NewHTTPServer creates a new HTTPServer
func NewHTTPServer() *HTTPServer {
	return &HTTPServer{
		Server: &http.Server{},
		Logger: rlog.Logger.WithField("com", "http-server"),
	}

}

func (s *HTTPServer) ListenAndServeContext(ctx context.Context, addr string, handler http.Handler) error {
	if ctx == nil {
		return errors.New("ctx must be set")
	}
	l, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	group, errCtx := errgroup.WithContext(ctx)
	s.waiterMux.Lock()
	defer s.waiterMux.Unlock()
	s.waiter = group
	s.Handler = handler
	group.Go(func() error {
		rlog.Logger.Debug(fmt.Sprintf("begin serve %s", addr))
		return s.Serve(l)
	})
	group.Go(func() error {
		<-errCtx.Done()
		s.Logger.Debug("got ctx error, try to close")
		return s.Close()
	})
	group.Go(func() error {
		<-ctx.Done()
		s.Logger.Debug("got ctx cancel, try to close")
		return s.Close()
	})
	return nil
}

func (s *HTTPServer) Close() error {
	s.waiterMux.Lock()
	defer s.waiterMux.Unlock()
	if s.waiter == nil {
		return errors.New("not started yet")
	}
	s.Logger.Debug("begin close http server")
	return s.Server.Close()
}

func (s *HTTPServer) Wait() error {
	s.waiterMux.Lock()
	unset := s.waiter == nil
	s.waiterMux.Unlock()
	if unset {
		return errors.New("not started yet")
	}
	s.waiterMux.Lock()
	wait := s.waiter.Wait
	s.waiterMux.Unlock()
	s.Logger.Debug("begin wait for http server to break")
	err := wait()
	if err == http.ErrServerClosed {
		err = nil //success
	}
	return err
}
